


#ifndef __CC_RENDERER_H_
#define __CC_RENDERER_H_

#include <vector>
#include <stack>

#include "platform/CCPlatformMacros.h"
#include "renderer/CCRenderCommand.h"
#include "renderer/CCGLProgram.h"
#include "CCGL.h"

#if !defined(NDEBUG) && CC_TARGET_PLATFORM == CC_PLATFORM_IOS

/// Basic wrapper for glInsertEventMarkerEXT() depending on the current build settings and platform.
#define CCGL_DEBUG_INSERT_EVENT_MARKER(__message__) glInsertEventMarkerEXT(0, __message__)
/// Basic wrapper for glPushGroupMarkerEXT() depending on the current build settings and platform.
#define CCGL_DEBUG_PUSH_GROUP_MARKER(__message__) glPushGroupMarkerEXT(0, __message__)
/// Basic wrapper for CCGL_DEBUG_POP_GROUP_MARKER() depending on the current build settings and platform.
#define CCGL_DEBUG_POP_GROUP_MARKER() glPopGroupMarkerEXT()

#else

#define CCGL_DEBUG_INSERT_EVENT_MARKER(__message__)
#define CCGL_DEBUG_PUSH_GROUP_MARKER(__message__)
#define CCGL_DEBUG_POP_GROUP_MARKER()

#endif

/**
 * @addtogroup renderer
 * @{
 */

NS_CC_BEGIN

class TrianglesCommand;
class MeshCommand;

/** Class that knows how to sort `RenderCommand` objects.
 Since the commands that have `z == 0` are "pushed back" in
 the correct order, the only `RenderCommand` objects that need to be sorted,
 are the ones that have `z < 0` and `z > 0`.
*/
class RenderQueue {
public:
    /**
    RenderCommand will be divided into Queue Groups.
    */
    enum QUEUE_GROUP
    {
        /**Objects with globalZ smaller than 0.*/
        GLOBALZ_NEG = 0,
        /**Opaque 3D objects with 0 globalZ.*/
        OPAQUE_3D = 1,
        /**Transparent 3D objects with 0 globalZ.*/
        TRANSPARENT_3D = 2,
        /**2D objects with 0 globalZ.*/
        GLOBALZ_ZERO = 3,
        /**Objects with globalZ bigger than 0.*/
        GLOBALZ_POS = 4,
        QUEUE_COUNT = 5,
    };

public:
    /**Constructor.*/
    RenderQueue();
    /**Push a renderCommand into current renderqueue.*/
    void push_back(RenderCommand* command);
    /**Return the number of render commands.*/
    ssize_t size() const;
    /**Sort the render commands.*/
    void sort();
    /**Treat sorted commands as an array, access them one by one.*/
    RenderCommand* operator[](ssize_t index) const;
    /**Clear all rendered commands.*/
    void clear();
    /**Realloc command queues and reserve with given size. Note: this clears any existing commands.*/
    void realloc(size_t reserveSize);
    /**Get a sub group of the render queue.*/
    inline std::vector<RenderCommand*>& getSubQueue(QUEUE_GROUP group) { return _commands[group]; }
    /**Get the number of render commands contained in a subqueue.*/
    inline ssize_t getSubQueueSize(QUEUE_GROUP group) const { return _commands[group].size();}

    /**Save the current DepthState, CullState, DepthWriteState render state.*/
    void saveRenderState();
    /**Restore the saved DepthState, CullState, DepthWriteState render state.*/
    void restoreRenderState();
    
protected:
    /**The commands in the render queue.*/
    std::vector<RenderCommand*> _commands[QUEUE_COUNT];
    
    /**Cull state.*/
    bool _isCullEnabled;
    /**Depth test enable state.*/
    bool _isDepthEnabled;
    /**Depth buffer write state.*/
    GLboolean _isDepthWrite;
};

//the struct is not used outside.
struct RenderStackElement
{
    int renderQueueID;
    ssize_t currentIndex;
};

class GroupCommandManager;

/* Class responsible for the rendering in.

Whenever possible prefer to use `TrianglesCommand` objects since the renderer will automatically batch them.
 */
class CC_DLL Renderer
{
public:
    /**The max number of vertices in a vertex buffer object.*/
    static const int VBO_SIZE = 65536;
    /**The max number of indices in a index buffer.*/
    static const int INDEX_VBO_SIZE = VBO_SIZE * 6 / 4;
    /**The rendercommands which can be batched will be saved into a list, this is the reserved size of this list.*/
    static const int BATCH_TRIAGCOMMAND_RESERVED_SIZE = 64;
    /**Reserved for material id, which means that the command could not be batched.*/
    static const int MATERIAL_ID_DO_NOT_BATCH = 0;
    /**Constructor.*/
    Renderer();
    /**Destructor.*/
    ~Renderer();

    //TODO: manage GLView inside Render itself
    void initGLView();

    /** Adds a `RenderComamnd` into the renderer */
    void addCommand(RenderCommand* command);

    /** Adds a `RenderComamnd` into the renderer specifying a particular render queue ID */
    void addCommand(RenderCommand* command, int renderQueue);

    /** Pushes a group into the render queue */
    void pushGroup(int renderQueueID);

    /** Pops a group from the render queue */
    void popGroup();

    /** Creates a render queue and returns its Id */
    int createRenderQueue();

    /** Renders into the GLView all the queued `RenderCommand` objects */
    void render();

    /** Cleans all `RenderCommand`s in the queue */
    void clean();

    /** Clear GL buffer and screen */
    void clear();

    /** set color for clear screen */
    void setClearColor(const CAColor4F& clearColor);
    /* returns the number of drawn batches in the last frame */
    ssize_t getDrawnBatches() const { return _drawnBatches; }
    /* RenderCommands (except) TrianglesCommand should update this value */
    void addDrawnBatches(ssize_t number) { _drawnBatches += number; };
    /* returns the number of drawn triangles in the last frame */
    ssize_t getDrawnVertices() const { return _drawnVertices; }
    /* RenderCommands (except) TrianglesCommand should update this value */
    void addDrawnVertices(ssize_t number) { _drawnVertices += number; };
    /* clear draw stats */
    void clearDrawStats() { _drawnBatches = _drawnVertices = 0; }

    /**
     * Enable/Disable depth test
     * For 3D object depth test is enabled by default and can not be changed
     * For 2D object depth test is disabled by default
     */
    void setDepthTest(bool enable);
    
    //This will not be used outside.
    inline GroupCommandManager* getGroupCommandManager() const { return _groupCommandManager; };

    /** returns whether or not a rectangle is visible or not */
    bool checkVisibility(const Mat4& transform, const DSize& size);

protected:

    //Setup VBO or VAO based on OpenGL extensions
    void setupBuffer();
    void setupVBOAndVAO();
    void setupVBO();
    void mapBuffers();
    void drawBatchedTriangles();

    //Draw the previews queued triangles and flush previous context
    void flush();
    
    void flush2D();
    
    void flush3D();

    void flushTriangles();

    void processRenderCommand(RenderCommand* command);
    void visitRenderQueue(RenderQueue& queue);

    void fillVerticesAndIndices(const TrianglesCommand* cmd);


    /* clear color set outside be used in setGLDefaultValues() */
    CAColor4F _clearColor;

    std::stack<int> _commandGroupStack;
    
    std::vector<RenderQueue> _renderGroups;

    MeshCommand* _lastBatchedMeshCommand;
    std::vector<TrianglesCommand*> _queuedTriangleCommands;

    //for TrianglesCommand
    ccV3F_C4B_T2F _verts[VBO_SIZE];
    GLushort _indices[INDEX_VBO_SIZE];
    GLuint _buffersVAO;
    GLuint _buffersVBO[2]; //0: vertex  1: indices

    // Internal structure that has the information for the batches
    struct TriBatchToDraw {
        TrianglesCommand* cmd;  // needed for the Material
        GLushort indicesToDraw;
        GLushort offset;
    };
    // capacity of the array of TriBatches
    int _triBatchesToDrawCapacity;
    // the TriBatches
    TriBatchToDraw* _triBatchesToDraw;

    int _filledVertex;
    int _filledIndex;

    bool _glViewAssigned;

    // stats
    ssize_t _drawnBatches;
    ssize_t _drawnVertices;
    //the flag for checking whether renderer is rendering
    bool _isRendering;
    
    bool _isDepthTestFor2D;
    
    GroupCommandManager* _groupCommandManager;
    
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
    CAObject* _notificationTarget;
#endif
};

NS_CC_END

/**
 end of support group
 @}
 */
#endif //__CC_RENDERER_H_
